package com.zhuoyue.web.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * @author zhanghui
 * @date 2021/9/27 9:12
 */
@Service
public class EncryptUtils {
    @Value("${encrypt.publicKey}")
    private String rsaPublicKey;

    @Value("${encrypt.privateKey}")
    private String rsaPrivateKey;


    /**
     * 生成ras公钥和私钥
     *
     * @return
     */
    public List<String> getRsaPublicAndPrivateKeyStr() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        List<String> keyStrList = new ArrayList<>();
        BASE64Encoder encoder = new BASE64Encoder();
        PublicKey publicKey = keyPair.getPublic();
        keyStrList.add(encoder.encode(publicKey.getEncoded()));

        PrivateKey privateKey = keyPair.getPrivate();
        keyStrList.add(encoder.encode(privateKey.getEncoded()));

        return keyStrList;
    }

    /**
     * 通过公钥字符串获取PublicKey对象
     *
     * @param publicKey
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    private PublicKey getPublicKey(String publicKey) throws InvalidKeySpecException, NoSuchAlgorithmException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 通过私钥字符串获取PrivateKey对象
     *
     * @param privateKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private PrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }


    public String base64Encode(byte[] data) {
        return Base64.getEncoder().encodeToString(data);
    }

    public byte[] base64Decode(String data) {
        return Base64.getDecoder().decode(data);
    }

    /**
     * ras加密, 返回加密后的base64字符串
     *
     * @param content 需要加密的字节数组
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public String rsaEncrypt(byte[] content) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(rsaPublicKey));
        return Base64.getEncoder().encodeToString(cipher.doFinal(content));
    }

    /**
     * ras解密
     *
     * @param content 密文, 若之前经过base64编码, 则需要先解码再调用该方法
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public String rasDecrypt(byte[] content) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
        Cipher cipher = Cipher.getInstance("RSA");
        PrivateKey privateKey = getPrivateKey(rsaPrivateKey);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(content));
    }
}
